import { camelize } from '../utils'
import { NodeTypes, ElementTypes, createRoot } from './ast'
import { isNativeTag, isVoidTag } from './index'
// 接收一个模板字符串
export function parse(content) {
    console.log(content);
    // 创建上下文
    const context = createParserContext(content)
    // 解析字符串
    const children = parseChildren(context)
    console.log(children);
    // 把ast放在一个Root里面
    return createRoot(children)
}

function createParserContext(content) {
    return {
        options: {
            delimiters: ['{{', '}}'],
            isNativeTag,
            isVoidTag
        },
        source: content
    }
}

function parseChildren(context) {
    const nodes = []
    // 没有遇到结束标识，就会一直循环
    while (!isEnd(context)) {
        const s = context.source
        let node
        // 解析节点
        // 遇到双大括号解析插值节点，遇到<解析元素节点，其他情况解析文本节点
        if (s.startsWith(context.options.delimiters[0])) {
            // 解析插值语法
            node = parseInterpolation(context)

        } else if (s[0] === '<') {
            // 解析元素节点
            node = parseElement(context)

        } else {
            // 解析文本节点
            node = parseText(context)
        }
        nodes.push(node)
    }
    let removedWhitespaces = false
    for (let i = 0; i < nodes.length; i++) {
        const node = nodes[i]
        if (node.type === NodeTypes.TEXT) {
            // 区分文本节点是否全是空白
            if (/[^\t\r\f\n ]/.test(node.content)) {
                // 文本节点有一些字符
                node.content = node.content.replace(/[\t\r\f\n ]+/g, ' ')
            } else {
                // 文本节点全是空白
                const prev = nodes[i - 1]
                const next = nodes[i + 1]
                if (!prev || !next || (prev.type === NodeTypes.ELEMENT && next.type === NodeTypes.ELEMENT && /[\r\n]/.test(node.content))) {
                    // 删除空白节点
                    nodes[i] = null
                    removedWhitespaces = true
                } else {
                    node.content = ' '
                }
            }
        }
    }
    return removedWhitespaces ? nodes.filter(Boolean) : nodes
}

// 解析插值语法
// <div id="foo" v-if="ok">hellp {{name}}</div>
// 进入这个函数时，上面的字符串已经被解析成{{name}}</div>
function parseInterpolation(context) {
    const [open, close] = context.options.delimiters
    // 去除前面的大括号
    advanceBy(context, open.length)

    // 拿到}}的索引
    const closeIndex = context.source.indexOf(close)
    const content = parseTextData(context, closeIndex).trim()

    // 吃掉后面的大括号}}
    advanceBy(context, close.length)
    return {
        type: NodeTypes.INTERPOLATION,
        content: {
            type: NodeTypes.SIMPLE_EXPRESSION,
            content,
            isStatic: false
        }
    }
}

// 遇到<，解析元素标签
function parseElement(context) {
    // parseTag(start)
    const element = parseTag(context)
    console.log(element);
    // 如果是自闭合标签,直接返回没有children，不用再做处理
    if (element.isSelfClosing || context.options.isVoidTag(element.tag)) {
        return element
    }
    // parseChildren
    element.children = parseChildren(context)
    // parseTag(end)
    parseTag(context)

    return element
}

function parseTag(context) {
    const match = /^<\/?([a-z][^\t\r\n\f />]*)/i.exec(context.source)
    // tag标签名就是match[1]
    const tag = match[1]
    // match[0]是<+标签名
    advanceBy(context, match[0].length)
    // 吃掉空格
    advanceSpaces(context)

    // 解析属性和指令
    const { props, directives } = parseAttributes(context)
    // 节点类型
    const tagType = isComponent(tag, context) ? ElementTypes.COMPONENT : ElementTypes.ELEMENT

    // 经过解析属性后，判断是否有/>，有就是自闭盒子
    // 是否是自闭盒子
    const isSelfClosing = context.source.startsWith('/>')
    // 是自闭盒吃掉2个'/>' 不是自闭盒子吃掉1个>
    advanceBy(context, isSelfClosing ? 2 : 1)
    return {
        type: NodeTypes.ELEMENT,
        tag,//标签名
        tagType,//是组件还是原生数组
        props,//属性节点数组
        directives,//指令数组
        isSelfClosing,//是否是自闭合标签
        children: [],
    }
}

// 判断是否是组件
function isComponent(tag, context) {
    return !context.options.isNativeTag(tag)
}

function parseAttributes(context) {
    const props = []
    const directives = []
    while (context.source.length && !context.source.startsWith('>') && !context.source.startsWith('/>')) {
        let attr = parseAttribute(context)

        // 如果是指令类型
        if (attr.type == NodeTypes.DIRECTIVE) {
            directives.push(attr)
        } else {
            // 否则就是属性类型
            props.push(attr)
        }
    }
    return { props, directives }
}

// 单个属性拆分
function parseAttribute(context) {
    const match = /^[^\t\r\n\f />][^\t\r\n\f />=]*/.exec(context.source)
    // 属性的名字
    const name = match[0]
    // 吃掉属性名
    advanceBy(context, name.length)
    // 吃掉属性名后的空格如果有的话
    advanceSpaces(context)

    let value
    if (context.source[0] === '=') {
        // 吃掉等号
        advanceBy(context, 1)
        // 再吃掉等号后的空格
        advanceSpaces(context)
        value = parseAttributeValue(context)
        // 吃掉键值对后的空格
        advanceSpaces(context)
    }
    // Directive指令节点
    // <div v-bind:class="myClass"></div>
    // <div :class="myClass"></div>
    // <div @click="handleClick"></div>
    let dirName, argContent
    if (/^(:|@|v-)/.test(name)) {
        // 如果以: @ v-开头则是指令
        if (name[0] === ':') {
            dirName = 'bind'
            argContent = name.slice(1)
        } else if (name[0] === '@') {
            dirName = 'on'
            argContent = name.slice(1)
        } else if (name.startsWith('v-')) {
            [dirName, argContent] = name.slice(2).split(':')
        }
        return {
            type: NodeTypes.DIRECTIVE,
            name: dirName,
            exp: value && {
                type: NodeTypes.SIMPLE_EXPRESSION,
                content: value.content,
                isStatic: false,
            }, // 表达式节点
            arg: argContent && {
                type: NodeTypes.SIMPLE_EXPRESSION,
                content: camelize(argContent),
                isStatic: true,
            } // 表达式节点
        }

    }
    // Attribute属性节点
    return {
        type: NodeTypes.ATTRIBUTE,
        name,
        value: value && {
            type: NodeTypes.TEXT,
            content: value.content,
        }
    }
}

// 解析属性值
function parseAttributeValue(context) {
    // 取出第一个引号"或者'
    const quote = context.source[0]
    advanceBy(context, 1)
    const endIndex = context.source.indexOf(quote)

    // 取出值
    const content = parseTextData(context, endIndex)
    // 吃掉最后一个引号
    advanceBy(context, 1)

    return { content }
}

function parseText(context) {
    // 结束标识
    const endTokens = ['<', context.options.delimiters[0]]
    let endIndex = context.source.length
    // 此时前面的标签已经解析完毕，前面的字符已经被吃了
    // 确定文本节点的结束位置,看是以<为结束还是{{为结束
    for (let i = 0; i < endTokens.length; i++) {
        let index = context.source.indexOf(endTokens[i])
        if (index !== -1 && index < endIndex) {
            endIndex = index
        }
    }
    const content = parseTextData(context, endIndex)
    return {
        type: NodeTypes.TEXT,
        content
    }
}

function parseTextData(context, length) {
    const text = context.source.slice(0, length)
    advanceBy(context, length)
    return text
}

function isEnd(context) {
    const s = context.source
    // s为''或者遇到</则结束
    return s.startsWith('</') || !s
}

function advanceBy(context, numberOfCharacters) {
    context.source = context.source.slice(numberOfCharacters)

}
// 消除空格
function advanceSpaces(context) {
    const match = /^[\t\r\n\f ]+/.exec(context.source)
    if (match) {
        advanceBy(context, match[0].length)
    }
}